#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>

#include "hj212_pack.h"


unsigned int HJ212_CRC16(const char *buf, int len)
{
    unsigned int r;
    unsigned char hi;
    char flag;
    int i, j;

    r = 0xffff;
    for (j = 0; j < len; j++)
    {
        hi = r >> 8;
        hi ^= buf[j];
        r = hi;

        for (i = 0; i < 8; i++)
        {
            flag = r & 0x0001;
            r = r >> 1;
            if (flag == 1) r ^= 0xa001;
        }
    }
    return r;
}

void hj212_generate_qn(char *buffer, int bufferlen, struct timeval precise_time)
{
    struct tm time;
    localtime_r(&precise_time.tv_sec, &time);

    snprintf(buffer, bufferlen, "%04d%02d%02d%02d%02d%02d%03d",
             time.tm_year + 1900, time.tm_mon + 1, time.tm_mday,
             time.tm_hour, time.tm_min, time.tm_sec, precise_time.tv_usec / 1000);
}

void hj212_pack_head_pnum(const char *qn, const char *sys_code,
                          int cn, const char *access_pwd,
                          const char *mn, int flag,
                          int pnum, int pno,
                          char *buffer, int bufferlen)
{
    if (qn == NULL)
        snprintf(buffer, bufferlen, "##0000ST=%s;CN=%d;PW=%s;MN=%s;Flag=%d;PNUM=%d;PNO=%d;CP=&&",
                 sys_code, cn, access_pwd, mn, flag, pnum, pno);
    else
        snprintf(buffer, bufferlen, "##0000QN=%s;ST=%s;CN=%d;PW=%s;MN=%s;Flag=%d;PNUM=%d;PNO=%d;CP=&&",
                 qn, sys_code, cn, access_pwd, mn, flag, pnum, pno);
}

void hj212_pack_head(const char *qn, const char *sys_code,
                     int cn, const char *access_pwd,
                     const char *mn, int flag,
                     char *buffer, int bufferlen)
{
    if (qn == NULL)
        snprintf(buffer, bufferlen, "##0000ST=%s;CN=%d;PW=%s;MN=%s;Flag=%d;CP=&&",
                 sys_code, cn, access_pwd, mn, flag);
    else
        snprintf(buffer, bufferlen, "##0000QN=%s;ST=%s;CN=%d;PW=%s;MN=%s;Flag=%d;CP=&&",
                 qn, sys_code, cn, access_pwd, mn, flag);
}

void hj212_pack_datatime(char *buffer, int bufferlen, time_t datatime)
{
    struct tm time;
    if (localtime_r(&datatime, &time))
    {
        snprintf(buffer, bufferlen,
                 "DataTime=%04d%02d%02d%02d%02d%02d;",
                 (time).tm_year + 1900, (time).tm_mon + 1, (time).tm_mday,
                 (time).tm_hour, (time).tm_min, (time).tm_sec);
        buffer[bufferlen - 1] = '\0';
    }
}

int _hj212_pack_add_field(char *buffer, int bufferlen, const char *format, va_list ap)
{
    int usedlen = strlen(buffer);
    int freelen = bufferlen - usedlen;
    if (freelen > 0)
    {
        char *free = &buffer[usedlen];
        int result = vsnprintf(free, freelen, format, ap);
        return result;
    }
    return -1;
}

int hj212_pack_add_field(char *buffer, int bufferlen, const char *format, ...)
{
    int result = 0;
    va_list ap;
    va_start(ap, format);
    result = _hj212_pack_add_field(buffer, bufferlen, format, ap);
    va_end(ap);
    return result;
}

void hj212_pack_rtdata(char *buffer, int bufferlen, const struct pol_rtval *pol)
{
    if (pol->enable_flag & HJ212_ZSRTD_ENABLE)
    {
        snprintf(buffer, bufferlen,
                 "%s-Rtd=%.*f,%s-ZsRtd=%.*f,%s-Flag=%s;",
                 pol->param.code, pol->param.decimal_num, pol->value,
                 pol->param.code, pol->param.decimal_num, pol->zs_value,
                 pol->param.code, (void *)&pol->datamark);
    }
    else
    {
        snprintf(buffer, bufferlen,
                 "%s-Rtd=%.*f,%s-Flag=%s;",
                 pol->param.code, pol->param.decimal_num, pol->value,
                 pol->param.code, (void *)&pol->datamark);
    }
}

void hj212_pack_hisdata(char *buffer, int bufferlen, const struct pol_his_data *pol)
{
    snprintf(buffer, bufferlen,
             "%s-Min=%.*f,%s-Avg=%.*f,%s-Max=%.*f",
             pol->param.code, pol->param.decimal_num, pol->min,
             pol->param.code, pol->param.decimal_num, pol->avg,
             pol->param.code, pol->param.decimal_num, pol->max);

    if (pol->enable_flag & HJ212_COU_ENABLE)
    {
        hj212_pack_add_field(buffer, bufferlen,
                             ",%s-Cou=%.*f",
                             pol->param.code, pol->param.decimal_num, pol->cou);
    }
    if (pol->enable_flag & HJ212_ZSHIS_ENABLE)
    {
        hj212_pack_add_field(buffer, bufferlen,
                             ",%s-ZsMin=%.*f,%s-ZsAvg=%.*f,%s-ZsMax=%.*f",
                             pol->param.code, pol->param.decimal_num, pol->zs_min,
                             pol->param.code, pol->param.decimal_num, pol->zs_avg,
                             pol->param.code, pol->param.decimal_num, pol->zs_max);
    }
    if (pol->enable_flag & HJ212_HISFLAG_ENABLE)
    {
        hj212_pack_add_field(buffer, bufferlen,
                             ",%s-Flag=%s",
                             pol->param.code, (void *)&pol->datamark);
    }
    hj212_pack_add_field(buffer, bufferlen, ";");
}

void hj212_pack_end(char *buffer, int bufferlen)
{
    char *p = strstr(buffer, "##");
    if (p)
    {
        char *len_field = p + 2;    // 数据段长度字段
        char *data_field = p + 6;   // 数据域

        int tmp = strlen(data_field) - 1;   // 去掉数据域的最后一个';'
        if (data_field[tmp] == ';')
        {
            data_field[tmp] = '\0';
        }
        strncat(buffer, "&&", bufferlen);           // 追加CP尾部
        int data_field_len = strlen(data_field);    // 计算数据域的长度

        char tmp_string[8];

        snprintf(tmp_string, sizeof(tmp_string), "%04d", data_field_len);       // 字节量
        memcpy(len_field, tmp_string, 4);

        uint16_t crc = HJ212_CRC16(data_field, data_field_len);                       // 计算CRC
        snprintf(tmp_string, sizeof(tmp_string), "%04X\r\n", crc);
        strncat(buffer, tmp_string, bufferlen);                                 // 追加CRC和包尾
    }
}

int hj212_unpack_string(const char *package, const char *keyword, const char *end, char *result, int result_maxlen)
{
    int err = -1;
    const char *p = strstr(package, keyword);
    if (p)
    {
        int keywordlen = strlen(keyword);
        const char *endpos = strstr(p + keywordlen, end);
        if ((endpos - p) > keywordlen)
        {
            strncpy(result, p + keywordlen, MIN(endpos - (p + keywordlen), result_maxlen));
            result[result_maxlen - 1] = '\0';
            err = 0;
        }
    }
    return err;
}

int hj212_unpack_int(const char *package, const char *keyword, const char *end, int *result)
{
    int err = -1;
    const char *p = strstr(package, keyword);
    if (p)
    {
        int keywordlen = strlen(keyword);
        const char *endpos = strstr(p + keywordlen, end);
        if ((endpos - p) > keywordlen)
        {
            *result = atoi(p + keywordlen);
            err = 0;
        }
    }
    return err;
}

uint32_t hj212_unpack(const char *buffer, int bufferlen, struct hj212_generalmsg *msg, int checkmark, const char **next)
{
    uint32_t result = UNPACK_NORMAL;

    char *packbegin = strstr(buffer, "##");     // 包起始
    if (!packbegin) // 无包头
    {
        return UNPACK_NO_HEAD;
    }
    int datalen = atoi(packbegin + 2);          // 数据域长度
    const char *databegin = packbegin + 6;      // 数据域起始
    const char *dataend = strstr(databegin, "\r\n");    // 数据域结束
    if (!dataend)
    {
        return UNPACK_NO_END;
    }

    /* 检查粘包 */
    if (!next)
    {
        *next = strstr(dataend, "##");
    }

    dataend -= 4;
    if (!(checkmark & UNPACK_NO_CHECK_DATALEN)) // 需要校验数据域长度
    {
        if ((dataend - databegin) != datalen)
        {
            result |= UNPACK_LEN_ERROR;
        }
    }
    if (!(checkmark & UNPACK_NO_CHECK_CRC)) // 需要校验数据域的CRC
    {
        uint16_t package_crc = atoi(dataend);
        uint16_t crc = HJ212_CRC16(databegin, dataend - databegin);
        if (crc != package_crc)
        {
            result |= UNPACK_CRC_ERROR;
        }
    }
    if (hj212_unpack_string(databegin, "QN=", ";", msg->parent.qn, HJ212_QN_MAX_LEN) < 0)
    {
        result |= UNPACK_NO_QN;
    }
    if (hj212_unpack_string(databegin, "ST=", ";", msg->parent.st, HJ212_SYSCODE_MAX_LEN) < 0)
    {
        result |= UNPACK_NO_ST;
    }
    if (hj212_unpack_int(databegin, "CN=", ";", &msg->parent.cn) < 0)
    {
        result |= UNPACK_NO_CN;
    }
    if (hj212_unpack_string(databegin, "PW=", ";", msg->parent.access_pwd, HJ212_ACCESS_PWD_MAX_LEN) < 0)
    {
        result |= UNPACK_NO_PW;
    }
    if (hj212_unpack_string(databegin, "MN=", ";", msg->parent.mn, HJ212_MN_MAX_LEN) < 0)
    {
        result |= UNPACK_NO_MN;
    }
    if (hj212_unpack_int(databegin, "Flag=", ";", &msg->parent.flag) < 0)
    {
        result |= UNPACK_NO_FLAG;
    }
    if (hj212_unpack_string(databegin, "CP=&&", "&&", msg->cp, HJ212_GENERAL_CP_MAX_LEN) < 0)
    {
        result |= UNPACK_NO_CP;
    }
    return result;
}














